package com.lanyeo.liver

import android.accessibilityservice.AccessibilityService
import android.accessibilityservice.GestureDescription
import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.content.Intent
import android.content.IntentFilter
import android.content.res.Configuration
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.Path
import android.graphics.PixelFormat
import android.hardware.display.DisplayManager
import android.hardware.display.VirtualDisplay
import android.media.ImageReader
import android.media.projection.MediaProjection
import android.media.projection.MediaProjectionManager
import android.os.Build
import android.os.Handler
import android.os.Message
import android.util.Log
import android.view.WindowManager
import android.view.accessibility.AccessibilityEvent
import android.widget.Toast
import androidx.core.app.NotificationCompat
import androidx.core.app.PendingIntentCompat
import com.lanyeo.liver.task.ITask
import com.lanyeo.liver.util.TouchAction
import com.lanyeo.liver.ui.ControlPanel
import com.lanyeo.liver.util.AppCrashHandler
import com.lanyeo.liver.util.Engine
import org.opencv.android.OpenCVLoader
import java.util.ArrayList

class WorkService : AccessibilityService(), MessageReceiver.ReceiveListener {
    private lateinit var mediaProjectionManager: MediaProjectionManager
    private lateinit var mediaProjection: MediaProjection
    private lateinit var receiver: MessageReceiver
    private var virtualDisplay: VirtualDisplay? = null
    private var screenRotation: Int = Configuration.ORIENTATION_LANDSCAPE
    // 屏幕截图的间隔
    private var nextCaptureTime: Long = Long.MAX_VALUE
    private var currentTask:ITask? = null
    private var controlPanel: ControlPanel? = null

    // 屏幕录制相关
    private var recordCode = -1
    private var recordData: Intent? = null
    private lateinit var mHandlerTouch: Handler

    // 服务是否已经在后台
    private var isServiceRunning = false
    /**
     * 动态注册广播
     */
    private fun registerReceiver() {
        receiver = MessageReceiver()
        val filter = IntentFilter()
        receiver.setReceiveListener(this)
        filter.addAction("android.intent.action.CONFIGURATION_CHANGED")
        filter.addAction(MessageReceiver.MSG)
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
            registerReceiver(receiver, filter)
        } else {
            registerReceiver(receiver, filter, RECEIVER_NOT_EXPORTED)
        }
    }

    override fun onCreate() {
        super.onCreate()
        registerReceiver()
        if (OpenCVLoader.initLocal()) {
            Log.d(TAG, "opencv load successful!")
        } else {
            Log.d(TAG, "opencv load fail! try load self!")
            System.loadLibrary("opencv_java4")
        }
        screenRotation = resources.configuration.orientation
        initHandlerTouch()
        val bundlePath = applicationContext.getExternalFilesDir("bundle")
        bundlePath.let {
            val isOK = Engine.initTess(bundlePath!!.absolutePath) && Engine.initNCNN(bundlePath!!.absolutePath)
            Toast.makeText(applicationContext, if (isOK) R.string.init_engine_ok else R.string.init_engine_fail, Toast.LENGTH_SHORT).show()
            Log.d(TAG, "init engine result: $isOK")
        }
        Thread.setDefaultUncaughtExceptionHandler(AppCrashHandler(applicationContext))
    }

    override fun onAccessibilityEvent(event: AccessibilityEvent?) {

    }

    override fun onInterrupt() {

    }


    private fun touchScreen(fingers: List<List<TouchAction>>) {
        val gestureBuilder = GestureDescription.Builder()
        for (finger in fingers) {
            var nextTime = 0L
            // Log.d(TAG, "touchScreen ${finger.size}")
            for (action in finger) {
                val path = Path()
                path.moveTo(action.start.x.toFloat(), action.start.y.toFloat())
                if (action.end.x != 0 || action.end.y != 0) {
                    path.lineTo(action.end.x.toFloat(), action.end.y.toFloat())
                }
                gestureBuilder.addStroke(GestureDescription.StrokeDescription(path, nextTime, action.durationTime.toLong(), action.isContinuous))
                if (!action.isContinuous) {
                    nextTime += action.durationTime + action.waitTime + 10
                }
            }
        }
        dispatchGesture(gestureBuilder.build(), object : GestureResultCallback() {
            override fun onCancelled(gestureDescription: GestureDescription?) {
                super.onCancelled(gestureDescription)
            }
            override fun onCompleted(gestureDescription: GestureDescription?) {
                super.onCompleted(gestureDescription)
            }
        }, null)
    }
    /**
     * 初始化触摸
     */
    private fun initHandlerTouch() {
        mHandlerTouch = object : Handler(mainLooper) {
            override fun handleMessage(msg: Message) {
                super.handleMessage(msg)
                touchScreen(msg.obj as List<List<TouchAction>>)
            }
        }
    }

    /**
     * 注销广播
     */
    private fun unregisterReceiver() {
        unregisterReceiver(receiver)
    }

    override fun onDestroy() {
        unregisterReceiver()
        stopTask()
        mediaProjection.stop()
        Engine.destroyTess()
        Engine.destroyNCNN()
        super.onDestroy()
    }


    /**
     * 创建前台通知
     */
    private fun createNotifyBar(): Notification {
        val manager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
        val channel = NotificationChannel(applicationContext.packageName, resources.getString(R.string.app_name), NotificationManager.IMPORTANCE_LOW).apply {
            setShowBadge(true)
            enableLights(false)
            enableVibration(false)
            setSound(null, null)
        }
        manager.createNotificationChannel(channel)
        return updateNotifyBar(resources.getString(R.string.app_running))
    }

    private fun updateNotifyBar(contentText: String, canClick: Boolean = false): Notification {
        val notification: NotificationCompat.Builder =
            NotificationCompat.Builder(this, applicationContext.packageName)
                .setDefaults(NotificationCompat.FLAG_ONGOING_EVENT or NotificationCompat.FLAG_NO_CLEAR)
                .setSmallIcon(R.mipmap.ic_launcher)
                .setLargeIcon(BitmapFactory.decodeResource(resources, R.mipmap.ic_launcher))
                .setPriority(NotificationCompat.PRIORITY_LOW)
                .setAutoCancel(false)
                .setContentText(contentText)
                .setOngoing(true)

        if (canClick) {
            val intent = Intent(applicationContext, MainActivity::class.java)
            val pendingIntent = PendingIntentCompat.getActivity(applicationContext, 0, intent, 0, false)
            notification.setContentIntent(pendingIntent)
        }
        return notification.build()
    }


    private var imageReader: ImageReader? =null
    private fun startTask(): Boolean {
        val destinyDpi = resources.configuration.densityDpi
        val windowManager = getSystemService(WindowManager::class.java)
        val screenWidth = windowManager.maximumWindowMetrics.bounds.width()
        val screenHeight = windowManager.maximumWindowMetrics.bounds.height()
        screenRotation = resources.configuration.orientation
        Log.d(TAG, "startTask -> screen[w:$screenWidth h:$screenHeight dpi:$destinyDpi]")

        imageReader = ImageReader.newInstance(screenWidth, screenHeight, PixelFormat.RGBA_8888, 2)

        virtualDisplay = mediaProjection.createVirtualDisplay("ScreenCapture", screenWidth, screenHeight, destinyDpi,
            DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY or DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC, imageReader!!.surface, null, null)

        imageReader!!.setOnImageAvailableListener({
            val  image = imageReader!!.acquireLatestImage()
            if (System.currentTimeMillis() < nextCaptureTime) {
                image?.close()
                return@setOnImageAvailableListener
            }
            if (image == null) {
                nextCaptureTime = System.currentTimeMillis() + 100
                return@setOnImageAvailableListener
            }
            if (currentTask == null || image.planes == null || image.planes.isEmpty()) {
                nextCaptureTime = System.currentTimeMillis() + 1000
            } else {
                val pixelStride = image.planes[0].pixelStride
                val rowPadding = image.planes[0].rowStride - pixelStride * image.width
                var bitmap = Bitmap.createBitmap(image.width + rowPadding / pixelStride, image.height, Bitmap.Config.ARGB_8888)
                bitmap.copyPixelsFromBuffer(image.planes[0].buffer)
                bitmap = Bitmap.createBitmap(bitmap, 0, 0, image.width, image.height)
                nextCaptureTime = currentTask!!.doTask(bitmap, screenRotation)
                if (nextCaptureTime < 0L) {
                    stopTask()
                    controlPanel!!.forceStopTask()
                }
            }
            image.close()
        }, null)
        return true
    }

    private fun stopTask() {
        virtualDisplay?.release()
        virtualDisplay = null
        nextCaptureTime = Long.MAX_VALUE
    }

    private fun createControlPanel(): Boolean {
        if (controlPanel == null) {
            controlPanel = ControlPanel(applicationContext)
            controlPanel!!.setScriptClickListener(onScriptClickListener)
            controlPanel!!.setTaskTouchListener(onTaskTouchListener)
            controlPanel!!.setButtonFlowHideListener(controlFlowButtonHideListener)
        } else {
            controlPanel?.reinit()
        }
        return true
    }


    private val controlFlowButtonHideListener = object : ControlPanel.OnButtonFlowHideListener {
        override fun click() {
            stopTask()
            mediaProjection.stop()
            val manager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
            manager.notify(NOTIFY_ID, updateNotifyBar(resources.getString(R.string.app_service_hide), true))
        }
    }

    private var onScriptClickListener = object : ControlPanel.OnScriptClickListener {
        override fun click(task: ITask?) {
            if (task == null) {
                currentTask = null
            } else {
                currentTask = task
                // 脚本开始
                if (startTask()) {
                    Toast.makeText(applicationContext, R.string.task_running_after, Toast.LENGTH_SHORT).show()
                    nextCaptureTime = System.currentTimeMillis() + 1000 * 3
                } else {
                    controlPanel!!.forceStopTask()
                }
            }
        }
    }

    private var onTaskTouchListener = object : ITask.OnTaskTouchListener {
        override fun onTouch(vararg fingers: List<TouchAction>) {
            mHandlerTouch.sendMessage(Message.obtain(mHandlerTouch, 1, fingers.asList()))
        }
    }

    override fun onMessage(intent: Intent) {
        if (intent.getStringExtra("type") == MessageType.RECORD.toString()) {
            recordCode = intent.getIntExtra("code", -1)
            recordData = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
                intent.getParcelableExtra("data")
            } else {
                intent.getParcelableExtra("data", Intent::class.java)
            }
            createControlPanel()
            if (isServiceRunning) {
                val manager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
                manager.notify(NOTIFY_ID, updateNotifyBar(resources.getString(R.string.app_running)))
            } else {
                startForeground(NOTIFY_ID, createNotifyBar())
            }
            // 该段代码只能在startForeground之后
            mediaProjectionManager = getSystemService(MediaProjectionManager::class.java)
            mediaProjection = mediaProjectionManager.getMediaProjection(recordCode, recordData!!)
        }

        // 屏幕发生旋转需要停止脚本
        if (resources.configuration.orientation != screenRotation) {
            if (currentTask != null && currentTask!!.isRunning()) {
                Toast.makeText(applicationContext, R.string.stop_task_by_screen_change, Toast.LENGTH_LONG).show()
            }
            screenRotation = resources.configuration.orientation
            stopTask()
            controlPanel?.forceStopTask()
        }
    }
    enum class MessageType {
        RECORD, // 开启录屏权限
    }
    companion object {
        const val TAG = "WorkService"
        const val NOTIFY_ID = 100
    }
}